import wx
import wx.xrc
import os
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt


#本程序的UI操作界面是采用wxPython编写，所以需要继承wx.Frame类，并创建类的实例
class MyFrame1(wx.Frame):

    #在构造函数中编写UI界面，并为按钮绑定相应的事件处理函数
    def __init__(self, parent, id=wx.ID_ANY, title=wx.EmptyString, pos=wx.DefaultPosition,
                 size=(800, 600), style=wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL|wx.VSCROLL):
        wx.Frame.__init__(self, parent, id=id, title=title, pos=pos,
                          size=size, style=style)

        #self.colorImg用来保存真彩色图像数组
        self.colorImg=np.ones((256,256))

        #self.grayImg用来保存灰度图的图像数组
        self.grayImg=np.ones((256,256))

        # self.binaryImg用来保存二值图的图像数组
        self.binaryImg=np.ones((256,256))

        self.SetSizeHints(wx.DefaultSize, wx.DefaultSize)

        bSizer1 = wx.BoxSizer(wx.VERTICAL)

        bSizer2 = wx.BoxSizer(wx.HORIZONTAL)

        self.m_button1 = wx.Button(self, wx.ID_ANY, u"选择、读取并显示图像", wx.DefaultPosition, wx.DefaultSize, 0)

        self.m_button1.SetBitmapPosition(wx.TOP)
        bSizer2.Add(self.m_button1, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)

        self.m_button2 = wx.Button(self, wx.ID_ANY, u"真彩图转灰度图", wx.DefaultPosition, wx.DefaultSize, 0)
        bSizer2.Add(self.m_button2, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)

        self.m_button3 = wx.Button(self, wx.ID_ANY, u"灰度图转二值图", wx.DefaultPosition, wx.DefaultSize, 0)
        bSizer2.Add(self.m_button3, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)

        self.m_button4 = wx.Button(self, wx.ID_ANY, u"绘制灰度图像的直方图", wx.DefaultPosition, wx.DefaultSize, 0)
        bSizer2.Add(self.m_button4, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)

        self.m_button5 = wx.Button(self, wx.ID_ANY, u"绘制彩色图像的直方图", wx.DefaultPosition, wx.DefaultSize, 0)
        bSizer2.Add(self.m_button5, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)

        self.m_button6 = wx.Button(self, wx.ID_ANY, u"绘制二值图像的直方图", wx.DefaultPosition, wx.DefaultSize, 0)
        bSizer2.Add(self.m_button6, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)

        bSizer1.Add(bSizer2, 0, wx.EXPAND, 5)

        bSizer3 = wx.BoxSizer(wx.VERTICAL)

        gSizer1 = wx.GridSizer(0, 2, 0, 0)

        self.m_bitmap1 = wx.StaticBitmap(self, wx.ID_ANY, wx.NullBitmap, wx.DefaultPosition, wx.DefaultSize, 0)
        gSizer1.Add(self.m_bitmap1, 1, wx.ALL | wx.EXPAND, 5)

        self.m_bitmap2 = wx.StaticBitmap(self, wx.ID_ANY, wx.NullBitmap, wx.DefaultPosition, wx.DefaultSize, 0)
        gSizer1.Add(self.m_bitmap2, 1, wx.ALL | wx.EXPAND, 5)

        self.m_bitmap3 = wx.StaticBitmap(self, wx.ID_ANY, wx.NullBitmap, wx.DefaultPosition, wx.DefaultSize, 0)
        gSizer1.Add(self.m_bitmap3, 1, wx.ALL | wx.EXPAND, 5)

        self.m_bitmap4 = wx.StaticBitmap(self, wx.ID_ANY, wx.NullBitmap, wx.DefaultPosition, wx.DefaultSize, 0)
        gSizer1.Add(self.m_bitmap4, 1, wx.ALL | wx.EXPAND, 0)

        bSizer3.Add(gSizer1, 1, wx.EXPAND, 5)

        bSizer1.Add(bSizer3, 1, wx.EXPAND, 5)

        self.SetSizer(bSizer1)
        self.Layout()

        self.Centre(wx.BOTH)

        #为相应每个按钮绑定事件处理函数
        self.m_button1.Bind(wx.EVT_BUTTON, self.chooseFile)
        self.m_button2.Bind(wx.EVT_BUTTON, self.rgbToGray)
        self.m_button3.Bind(wx.EVT_BUTTON, self.grayToBinary)
        self.m_button4.Bind(wx.EVT_BUTTON, self.drawGray)
        self.m_button5.Bind(wx.EVT_BUTTON, self.drawColor)
        self.m_button6.Bind(wx.EVT_BUTTON, self.drawBinary)


    # 实现所有事件处理函数

    #选择图像文件并将其读取，然后显示到UI界面上，该方法实现了图像的读取与显示
    def chooseFile(self, event):
        fd = wx.FileDialog(None, "请选择图像文件", os.getcwd(), wildcard="All files (*.*)|*.*",
                           style=wx.DD_DEFAULT_STYLE | wx.DD_NEW_DIR_BUTTON)
        if fd.ShowModal() == wx.ID_OK:
            #windows系统中文件路径分隔符是'\',但是编程语言和Linux下一般都是'/'，所以用正则进行替换
            self.fileName = fd.GetPath().replace('\\','/')
            print(self.fileName)
        curImg=cv.imread(self.fileName)
        height, width = curImg.shape[:2][::-1]
        if height>342 or width>342:
            #等比例缩小图片，不然太大了显示不下
            curImg=cv.resize(curImg,(0,0),fx=0.3,fy=0.3,interpolation=cv.INTER_NEAREST)
        height,width=curImg.shape[:2]
        pic = wx.Bitmap.FromBuffer(width, height, self.BGR2RGB(curImg))
        self.m_bitmap1.SetBitmap(pic)
        np.resize(self.colorImg,(height,width))
        self.colorImg=curImg

    #该方法将真彩色图转换成灰度图，由于不允许直接使用库函数，所以遍历每个像素点，并对其进行gamma校正,gamma值取2.2
    def rgbToGray(self, event):
        gi = self.colorImg.copy()
        w, h = gi.shape[:2][::-1]
        for i in range(0, h):
            for j in range(0, w):
                x = int((1 * gi[i, j][2]) ** 2.2) + int((1.5 * gi[i, j][1]) ** 2.2) + int((0.6 * gi[i, j][0]) ** 2.2)
                y = 1 ** 2.2 + 1.5 ** 2.2 + 0.6 ** 2.2
                gi[i, j] = int((x / y) ** (1 / 2.2))
        pic = wx.Bitmap.FromBuffer(w, h, self.BGR2RGB(gi))
        self.m_bitmap2.SetBitmap(pic)
        np.resize(self.grayImg,(w,h))
        self.grayImg=gi



    #将灰度图转换成二值图，也因为不许调库，所以遍历每个像素点，将小于平均值得像素点置为0，大于的置为255
    def grayToBinary(self, event):
        bi = self.grayImg.copy()
        w, h = bi.shape[:2][::-1]
        avg = int(np.average(bi))
        for i in range(0, h):
            for j in range(0, w):
                if any(bi[i, j] <= avg):
                    bi[i, j] = 0
                else:
                    bi[i, j] = 255
        pic = wx.Bitmap.FromBuffer(w, h, self.BGR2RGB(bi))
        self.m_bitmap3.SetBitmap(pic)
        np.resize(self.binaryImg, (w, h))
        self.binaryImg = bi

    #绘制灰度图像直方图，在Pyhon中可以直接看到画的Plots，但是为了保持统一，
    #我将绘制的直方图图像保存了下来，然后再读取该图像，再渲染到UI界面上
    def drawGray(self, event):
        plt.hist(self.colorImg.ravel(), 256, [0, 256])
        plt.xlim([0, 256])
        plt.savefig("ansGray.png")
        plt.show()
        ans=cv.imread("ansGray.png")
        w, h = ans.shape[:2][::-1]
        ans=cv.resize(ans,(0,0),fx=0.6,fy=0.6,interpolation=cv.INTER_NEAREST)
        w, h = ans.shape[:2][::-1]
        pic = wx.Bitmap.FromBuffer(w, h, self.BGR2RGB(ans))
        self.m_bitmap4.SetBitmap(pic)

    #绘制真彩图的图像直方图
    def drawColor(self,event):
        chans = cv.split(self.colorImg)
        colors = ('b', 'g', 'r')
        # plt.figure()
        for (chan, color) in zip(chans, colors):
            hist = cv.calcHist([chan], [0], None, [256], [0, 256])
            plt.plot(hist, color=color)
            plt.xlim([0, 256])
        plt.savefig("ansColor.png")
        plt.show()
        ansColor=cv.imread("ansColor.png")
        w, h = ansColor.shape[:2][::-1]
        ans = cv.resize(ansColor, (int(w * 0.6), int(h * 0.6)), interpolation=cv.INTER_CUBIC)
        w, h = ans.shape[:2][::-1]
        pic = wx.Bitmap.FromBuffer(w, h, self.BGR2RGB(ans))
        self.m_bitmap4.SetBitmap(pic)

    #绘制二值图的图像直方图
    def drawBinary(self,event):
        plt.hist(self.binaryImg.ravel(), 256, [0, 256])
        plt.xlim([0, 256])
        plt.savefig("ansBinary.png")
        plt.show()
        ans = cv.imread("ansBinary.png")
        w, h = ans.shape[:2][::-1]
        ans = cv.resize(ans, (int(w * 0.6), int(h * 0.6)), interpolation=cv.INTER_CUBIC)
        w, h = ans.shape[:2][::-1]
        pic = wx.Bitmap.FromBuffer(w, h, self.BGR2RGB(ans))
        self.m_bitmap4.SetBitmap(pic)

    #下面这个函数将BGR图像转换成RGB，因为在opencv中是按bgr的通道顺序，
    #但是在wxPython中的Bitmap是按照rgb的通道顺序，为了在UI界面上显示，
    #所以需要转换
    def BGR2RGB(self,src):
        (B, G, R) = cv.split(src)
        img = cv.merge([R, G, B])
        return img

#主函数，程序的入口
if __name__ == '__main__':
    app=wx.App()
    mainWindow=MyFrame1(None,title="实验二",size=(1200,700))
    mainWindow.Show(True)
    app.MainLoop()


